#ifndef __SINGAPOREBUSGENERICVIEW__
#define __SINGAPOREBUSGENERICVIEW__

// [[[ begin generated region: do not modify [Generated Includes]
#include <aknview.h>
// ]]] end generated region [Generated Includes]

#if !defined(__SERIES60_3X__) || defined(__S60_50__)
#include <akntoolbarobserver.h>
#include <akntoolbar.h>
#include <aknlongtapdetector.h>
#include <akntitlepaneobserver.h>
#endif

#include "googlepositioning.h"
#include "positionrequest.h"
#include "SingaporeBusStructures.h"
#include "AsyncFileLoader.h"
#include "IteratorControl.h"
#include "ArrivalTimeQuery.h"
#include "IrisQuery.h"
#include "SmrtQuery.h"
#include "CSingaporeBusIteratorView.h"
#include "Function.h"
#include "Binder.h"
#include "CharUtils.h"
#include "akniconarray.h"
#include "CSingaporeBusGenericView.hrh"
#include "CSingaporeBusListBox.h"

struct CStoreViewState
	{
	virtual void StoreViewStateL(TDes8& aCustomMessage) = 0;
	~CStoreViewState() {}
	};

void StoreCustomMessageParamL(TDes8& aCustomMessage, const TDesC8& aField, TRefByValue<const TDesC8> aFormat, ...);
TBool ParseCustomMessageParamL(TPtrC8& aField, TPtrC8& aValue, TPtrC8& aRemainder);

template<class T, class C>
class CSingaporeBusGenericView;

struct CArrivalTimeItem;

struct CArrivalTimeItemCallback
	{
	virtual void ArrivalTimeItemCallback(CArrivalTimeItem*) = 0;
	~CArrivalTimeItemCallback() { }
	};

struct CArrivalTimeItem : public CBase
	{
	HBufC* iNextBus;
	HBufC* iSubsequentBus;
	HBufC* iFormat;
	CArrivalTimeQuery::TServiceRequestStatus iRequestStatus;
	const TService* iService;
	const TStation* iStation;
	CArrivalTimeQuery* iArrivalTimeQuery;
	CArrivalTimeItemCallback* iCallback;
	CArrivalTimeItem();
	~CArrivalTimeItem();
	};

struct CSingaporeBusGenericHandler
	{
	
	static TBool FilterItem(const TStation* aItem, const TDesC8& aSearchText);
	static TBool FilterItem(const TService* aItem, const TDesC8& aSearchText);
	static TBool FilterItem(const CArrivalTimeItem* aItem, const TDesC8& aSearchText);
	
	static TReal32 OrderItem(const TStation* aItem);
	static TReal32 OrderItem(const TService* aItem);
	static TReal32 OrderItem(const CArrivalTimeItem* aItem);
	
	static TBool ListBoxObserver(const TStation* aItem, CEikListBox* aListBox, MEikListBoxObserver::TListBoxEvent aEventType);
	static TBool ListBoxObserver(const TService* aItem, CEikListBox* aListBox, MEikListBoxObserver::TListBoxEvent aEventType);
	static TBool ListBoxObserver(const CArrivalTimeItem* aItem, CEikListBox* aListBox, MEikListBoxObserver::TListBoxEvent aEventType);
	
	static TBool HandleCommandL(const TStation* aItem, TInt aCommand );
	static TBool HandleCommandL(const TService* aItem, const TStation* aStation, TInt aCommand );
	static TBool HandleCommandL(const CArrivalTimeItem* aItem, TInt aCommand );
	
	
	static CArrivalTimeItem* NewArrivalTimeItemL(const TStation* aStation, const TService *aService, CArrivalTimeItemCallback* aCallback);
	static void IrisCallback(CArrivalTimeItem* aItem, CIrisQuery*, const TDesC8&, const TDesC8&, CArrivalTimeQuery::TServiceRequestStatus, const TDesC&, const TDesC8&, const TDesC8&);
	static void SmrtCallback(CArrivalTimeItem* aItem, CSmrtQuery*, const TDesC8&, const TDesC8&, CArrivalTimeQuery::TServiceRequestStatus, const TDesC&, const TDesC8&, const TDesC8&);
	static void TflCallback(CArrivalTimeItem* aItem, CTflQuery*, const TDesC8&, const TDesC8&, CArrivalTimeQuery::TServiceRequestStatus, const TDesC&, const TDesC8&, const TDesC8&);
	
	};


template<class T, class C>
class CSingaporeBusGenericView : public CSingaporeBusIteratorView<T>, public CArrivalTimeItemCallback, public CStoreViewState
#if !defined(__SERIES60_3X__) || defined(__S60_50__)
, public MAknToolbarObserver
, public MAknLongTapDetectorCallBack
, public MAknTitlePaneObserver
#endif
	{
public:
	
#if !defined(__SERIES60_3X__) || defined(__S60_50__)
	// http://www.developer.nokia.com/Community/Wiki/Working_with_LongTapDetector_API_-_S60_Touch_UI
	LManagedPtr<CAknLongTapDetector> iLongTapDetector;
	TBool iLongTapUnderWay;
	virtual void HandleLongTapEventL(const TPoint& aPenEventLocation, const TPoint& aPenEventScreenLocation)
		{
		TFunTrace funTrace(__PRETTY_FUNCTION__);
		iLongTapUnderWay = ETrue;
		this->MenuBar()->SetContextMenuTitleResourceId(iContextMenuResId);
		this->MenuBar()->TryDisplayContextMenuBarL();
		}
	TBool LongTapPointerEventL(const TPointerEvent& aPointerEvent)
		{
		TFunTrace funTrace(__PRETTY_FUNCTION__);
		//if(aPointerEvent.iType != TPointerEvent::EButton1Down)
		//	PrintNote2L(_L("LongTapPointerEventL(%d)"), aPointerEvent.iType);
		iLongTapDetector->PointerEventL(aPointerEvent);
		if(iLongTapUnderWay)
			{
			if(aPointerEvent.iType == TPointerEvent::EButton1Up)
				iLongTapUnderWay = EFalse;
			return ETrue;
			}
		else
			return EFalse; // let the listbox handle the event too
		}
	virtual void HandleTitlePaneEventL(TInt  aEventID)
		{
	    if (aEventID == EAknTitlePaneTapped)
	    {
		  // Custom action on title pane tapping event
	    if(this->iContainer && this->iContainer->iIteratorControl->iListBox->Model()->NumberOfItems() > 0)
	    	{
			this->iContainer->iIteratorControl->iListBox->SetCurrentItemIndex(0);
			this->iContainer->iIteratorControl->iListBox->ScrollToMakeItemVisible(
					this->iContainer->iIteratorControl->iListBox->CurrentItemIndex()
					);
			this->iContainer->iIteratorControl->iListBox->DrawDeferred();
	    	}
	    }
	    else
	    {
	          // No implementation's required at the moment
	    }		
		}
	
#endif
	
	LManagedPtr< CIterator<T> > iUnderlying, iFilterIterator;
	LManagedPtr< COrderingIterator<T, TReal32> > iOrderIterator;
	
	virtual void FormatItem(TDes& aBuf, T aItem) = 0;
	virtual void DrawItem(CSingaporeBusListBoxItemDrawer* aDrawer, T aItem,
			TInt aItemIndex, const TRect& aActualItemRect,
			TBool aItemIsCurrent, TBool aViewIsEmphasized, TBool aViewIsDimmed, 
			TBool aItemIsSelected) { };
	virtual TBool FilterItem(T aItem, const TDesC8& aFilter) = 0;
	virtual TReal32 OrderItem(T aItem) = 0;
	virtual TBool ListBoxObserver(T aItem, CEikListBox* aListBox, MEikListBoxObserver::TListBoxEvent aEventType) = 0;
	virtual TBool HandleCommandL(T aItem, TInt aCommand ) = 0;

	TInt iContextMenuResId, iIconArrayResId;
	
	void RestoreViewStateL(const TDesC8& aCustomMessage)
		{
		TFunTrace funTrace(__PRETTY_FUNCTION__);
		
		if(1==0)
		{
		// we hide the search field if there is no search field
		TPtrC8 field, value, remainder(aCustomMessage);
		while(ParseCustomMessageParamL(field, value, remainder) && field != _L8("txt"));
		if(field != _L8("txt") && this->iContainer->iIteratorControl->iSearchField->IsVisible())
			{
			this->iSearchText.Copy(KNullDesC());
			(*this->iContainer->iIteratorControl->iSearchTextChangedFun)(*narrowToWideLC(this->iSearchText), MCoeControlObserver::EEventRequestCancel);
			this->iContainer->iIteratorControl->HideSearchFieldL();
			this->iContainer->iIteratorControl->iListBox->HandleItemRemovalL();
			this->iContainer->iIteratorControl->iListBox->HandleItemAdditionL();
			}
		}
		
		TPtrC8 field, value, remainder(aCustomMessage);
		while(ParseCustomMessageParamL(field, value, remainder))
			{
			if(field == _L8("top"))
				{
				TLex8 lex(value);
				TInt index;
				lex.Val(index);
				this->iContainer->iIteratorControl->iListBox->SetTopItemIndex(index);
				}
			if(field == _L8("cur"))
				{
				TLex8 lex(value);
				TInt index;
				lex.Val(index);
				this->iContainer->iIteratorControl->iListBox->SetCurrentItemIndex(index);
				}
			if(field == _L8("txt"))
				{
				this->iSearchText.Copy(value);
				this->iContainer->iIteratorControl->iSearchText.Copy(value);
				this->iContainer->iIteratorControl->iSearchField->SetSearchTextL(*narrowToWideLC(value));
				CleanupStack::PopAndDestroy();
				this->iContainer->iIteratorControl->ShowSearchFieldL();
				(*this->iContainer->iIteratorControl->iSearchTextChangedFun)(*narrowToWideLC(this->iSearchText), MCoeControlObserver::EEventStateChanged);
				CleanupStack::PopAndDestroy();
				this->iContainer->iIteratorControl->iListBox->HandleItemRemovalL();
				this->iContainer->iIteratorControl->iListBox->HandleItemAdditionL();
				}
			}
		}
	void StoreViewStateL(TDes8& aBuf)
		{
		TFunTrace funTrace(__PRETTY_FUNCTION__);
		if(this->iContainer->iIteratorControl->iListBox->TopItemIndex() != -1)
			StoreCustomMessageParamL(aBuf, _L8("top"), _L8("%d"), this->iContainer->iIteratorControl->iListBox->TopItemIndex());
		if(this->iContainer->iIteratorControl->iListBox->CurrentItemIndex() != -1)
			StoreCustomMessageParamL(aBuf, _L8("cur"), _L8("%d"), this->iContainer->iIteratorControl->iListBox->CurrentItemIndex());
		if(this->iSearchText.Length())
			StoreCustomMessageParamL(aBuf, _L8("txt"), _L8("%S"), &this->iSearchText);
		}
	
	virtual void ArrivalTimeItemCallback(CArrivalTimeItem*)
		{
		TFunTrace funTrace(__PRETTY_FUNCTION__);
		if(this->iContainer)
			{
			//this->iContainer->iIteratorControl->iListBox->HandleItemAdditionL();
			//this->iContainer->iIteratorControl->iListBox->DrawNow();
			this->iContainer->iIteratorControl->iListBox->DrawDeferred();
			}
		}
	
	virtual void ListBoxObserverT(CEikListBox* aListBox, MEikListBoxObserver::TListBoxEvent aEventType)
		{
		T item;
		if(		this->iContainer->iIteratorControl->iListBox->CurrentItemIndex() >= 0
				&& this->iContainer->iIteratorControl->GetItemL(this->iContainer->iIteratorControl->iListBox->CurrentItemIndex(), item)
				&& this->ListBoxObserver(item, aListBox, aEventType));
			return;
		}
	virtual void HandleCommandL( TInt aCommand )
		{
		TFunTrace funTrace(__PRETTY_FUNCTION__);
		T item;
		if(		this->iContainer
				&& this->iContainer->iIteratorControl->iListBox->CurrentItemIndex() >= 0
				&& this->iContainer->iIteratorControl->GetItemL(this->iContainer->iIteratorControl->iListBox->CurrentItemIndex(), item)
				&& this->HandleCommandL(item, aCommand)
			)
			return;
		switch ( aCommand )
			{
				case ESingaporeBusSearch:
				if(this->iContainer)
					this->iContainer->iIteratorControl->ShowSearchFieldL();
				return;
			}
		CSingaporeBusIteratorView<T>::HandleCommandL(aCommand);
		}
	
	TKeyResponse OfferKeyEventL(const TKeyEvent& aKeyEvent, TEventCode aType)
		{
		TFunTrace funTrace(__PRETTY_FUNCTION__);
		switch(aKeyEvent.iScanCode)
			{
				case EStdKeyRightShift: case EStdKeyLeftShift: case EStdKeyHash:
					if(aType == EEventKeyDown)
						{
						this->MenuBar()->SetContextMenuTitleResourceId(iContextMenuResId);
						this->MenuBar()->TryDisplayContextMenuBarL();
						}
					return EKeyWasConsumed;
					break;
				default:
					return EKeyWasNotConsumed;
			}
		}
	
	void OfferToolbarEventL(TInt aCommand)
	{
	TFunTrace funTrace(__PRETTY_FUNCTION__);
	HandleCommandL(aCommand); // In this example we are simply calling HandleCommandL by passing received command
	}	
	
	static C* NewL()
	{
		TFunTrace funTrace(__PRETTY_FUNCTION__);
		C* self = NewLC();
		CleanupStack::Pop( self );
		return self;
	}
	static C* NewLC()
	{
		TFunTrace funTrace(__PRETTY_FUNCTION__);
		C* self = new ( ELeave ) C();
		CleanupStack::PushL( self );
		self->ConstructL();
		return self;
	}
	
	/*
LCleanedupPtr<scalp::CFunction<void (int, const TRect &, int, int, int, int)>, TPtrCleanupStrategy>].LCleanedupPtr(
	scalp::TBinder<7, void, scalp::TMemFun7<void, CIteratorControl<const CArrivalTimeItem *>, int, const TRect &, int, int, int, int>, scalp::TListCons<CIteratorControl<const CArrivalTimeItem *> *, scalp::TListCons<scalp::TPlaceholder<0>, scalp::TListCons<scalp::TPlaceholder<1>, scalp::TListCons<scalp::TPlaceholder<2>, scalp::TListCons<scalp::TPlaceholder<3>, scalp::TListCons<scalp::TPlaceholder<4>, scalp::TListCons<scalp::TPlaceholder<5>, scalp::TNil>>>>>>>>)
	
	' does not match	CSingaporeBusGenericView.h	SingaporeBus/inc	line 281	C/C++ Problem
	 */
	
	virtual CEikListBox* ConstructListBoxL(CSingaporeBusListBox* aListBox, TInt aItemHeight)
		{
		aListBox->ConstructL(
				scalp::MakeFunctionL( scalp::Bind(&CIteratorControl<T>::DrawActualItem, this->iContainer->iIteratorControl, scalp::Free2, scalp::Free3, scalp::Free4, scalp::Free5, scalp::Free6, scalp::Free7) ),
				this->iContainer->iIteratorControl,
				this->iContainer->iIteratorControl);
		//aListBox->SetMopParent(this->iContainer.Get());
		//#ifndef __SERIES60_3X__ // Symbian^3 does not define that macro
#if !defined(__SERIES60_3X__) || defined(__S60_50__) // except for S60 3rd
		aListBox->SetBackground(this->iContainer.Get()); // important for scrolling
#endif
		aListBox->SetItemHeightL(aItemHeight);
		CAknIconArray* icons = new(ELeave) CAknIconArray(2);
		CleanupStack::PushL(icons);
		icons->ConstructFromResourceL(iIconArrayResId);
		aListBox->ItemDrawer()->SetIconArray(icons);
		CleanupStack::Pop(); // icons
		this->iContainer->iIteratorControl->iDrawItemFun = scalp::MakeFunctionL( scalp::Bind(&CSingaporeBusGenericView<T,C>::DrawItem, this, aListBox->ItemDrawer(), scalp::Free1, scalp::Free2, scalp::Free3, scalp::Free4, scalp::Free5, scalp::Free6, scalp::Free7) );
		return aListBox;
		}
	

	virtual CEikListBox* ConstructListBoxL(CEikFormattedCellListBox* aListBox)
		{
		aListBox->ConstructL(this->iContainer.Get(), EAknListBoxSelectionList);
		aListBox->Model()->SetOwnershipType(ELbmDoesNotOwnItemArray);
		delete aListBox->Model()->ItemTextArray();
		aListBox->Model()->SetItemTextArray(this->iContainer->iIteratorControl);
		aListBox->ItemDrawer()->FormattedCellData()->EnableMarqueeL(ETrue);
		// http://www.developer.nokia.com/Community/Wiki/TSS000022_-_Using_CAknIconArray_to_read_icons_for_listbox_from_resources
		// http://www.developer.nokia.com/Community/Discussion/showthread.php?111640-CAknIconArray-from-resource
		CAknIconArray* icons = new(ELeave) CAknIconArray(2);
		CleanupStack::PushL(icons);
		icons->ConstructFromResourceL(iIconArrayResId);
		aListBox->ItemDrawer()->ColumnData()->SetIconArray(icons);
		CleanupStack::Pop(); // icons
		this->iContainer->iIteratorControl->iFormatFun = scalp::MakeFunctionL( scalp::Bind(&CSingaporeBusGenericView<T,C>::FormatItem, this, scalp::Free1, scalp::Free2) );
    	return aListBox;
		}
	
	void ConstructL(TInt aResId, TInt aContextMenuResId, TInt aIconArrayResId, TInt aId)
		{
		TFunTrace funTrace(__PRETTY_FUNCTION__);
		CSingaporeBusIteratorView<T>::ConstructL(aResId, aId);
		iContextMenuResId = aContextMenuResId;
		iIconArrayResId = aIconArrayResId;
#if !defined(__SERIES60_3X__) || defined(__S60_50__)
		iLongTapUnderWay = EFalse;
#endif
		}

	
	void SetUnderlyingL(CIterator<T>* aIterator)
		{
		TFunTrace funTrace(__PRETTY_FUNCTION__);
		iUnderlying = aIterator;
		iOrderIterator = new (ELeave) COrderingIterator<T, TReal32>(
				iUnderlying.Get(),
				scalp::MakeFunctionL( scalp::Bind(&CSingaporeBusGenericView<T,C>::OrderItem, this, scalp::Free1) )
				);
		iFilterIterator = new (ELeave) CFilteringIterator<T>(
				iOrderIterator.Get(),
				scalp::MakeFunctionL( scalp::Bind(&CSingaporeBusGenericView<T,C>::FilterItem, this, scalp::Free1, scalp::Ref(this->iSearchText)) )
				);
		if(this->iContainer)
			this->iContainer->iIteratorControl->SetUnderlying( iFilterIterator.Get() );	
		}
	

	void SetEmptyUnderlyingL()
		{
		TFunTrace funTrace(__PRETTY_FUNCTION__);
		iUnderlying = NULL;
		iOrderIterator = NULL;
		iFilterIterator = NULL;
		if(this->iContainer)
			this->iContainer->iIteratorControl->SetUnderlying( &CIteratorControl<T>::EmptyUnderlying );	
		}
	
	
	void NotifyPositionChange()
		{
		TFunTrace funTrace(__PRETTY_FUNCTION__);
		if(iOrderIterator)
			iOrderIterator->SetDirty(ETrue);
		CSingaporeBusIteratorView<T>::NotifyPositionChange();
		}

	void NotifyConnected()
		{
		TFunTrace funTrace(__PRETTY_FUNCTION__);
		
		}
						
		
protected:
	void DoActivateL(
		const TVwsViewId& aPrevViewId,
		TUid aCustomMessageId,
		const TDesC8& aCustomMessage )
		{
		TFunTrace funTrace(__PRETTY_FUNCTION__);
#if !defined(__SERIES60_3X__) || defined(__S60_50__)
	    if(this->Toolbar())
	    	{
	    	this->Toolbar()->SetToolbarObserver(this);
			this->Toolbar()->SetToolbarVisibility(ETrue, EFalse);
	    	}
#endif
		CSingaporeBusIteratorView<T>::DoActivateL( aPrevViewId, aCustomMessageId, aCustomMessage );
		this->iContainer->iIteratorControl->iSearchTextChangedFun = scalp::MakeFunctionL( scalp::Bind(&CSingaporeBusGenericView<T,C>::SearchTextChanged, this, scalp::Free1, scalp::Free2) );
		this->iContainer->iIteratorControl->iListBoxObserverFun = scalp::MakeFunctionL( scalp::Bind(&CSingaporeBusGenericView<T,C>::ListBoxObserverT, this, scalp::Free1, scalp::Free2) );
		this->iContainer->iOfferKeyEventFun = scalp::MakeFunctionL( scalp::Bind(&CSingaporeBusGenericView<T,C>::OfferKeyEventL, this, scalp::Free1, scalp::Free2) );
		if(this->iFilterIterator)
			{
			this->iFilterIterator->Reset();
			this->iContainer->iIteratorControl->SetUnderlying( iFilterIterator.Get() );
			}
#if !defined(__SERIES60_3X__) || defined(__S60_50__)
	    this->iLongTapDetector = CAknLongTapDetector::NewL(this);
		this->iContainer->iHandlePointerEventFun = scalp::MakeFunctionL( scalp::Bind(&CSingaporeBusGenericView<T,C>::LongTapPointerEventL, this, scalp::Free1) );
	    
		//http://www.developer.nokia.com/Community/Wiki/Title_Pane_Touch_Observer_API
	    CEikStatusPane* statusPane = STATIC_CAST( CAknAppUi*, CEikonEnv::Static()->EikAppUi() )->StatusPane();
	    if (statusPane)
	    {
	       // Title pane UID
	       TUid titlePaneUid; 
	       titlePaneUid.iUid = EEikStatusPaneUidTitle;
	       // Get pointer to CAknTitlePane object 
	       CAknTitlePane* titlePane = (CAknTitlePane*) statusPane->ControlL(titlePaneUid);
	       // Set this AppUi object as the observer of the title pane
	       titlePane->SetTitlePaneObserver(this);
	    }
#endif
		}
	
	void DoDeactivate()
		{
		CSingaporeBusIteratorView<T>::DoDeactivate();
#if !defined(__SERIES60_3X__) || defined(__S60_50__)
		if(this->Toolbar())
			this->Toolbar()->SetToolbarVisibility(EFalse);
#endif
		}
	
		
	};

#endif // __SINGAPOREBUSGENERICVIEW__
